package com.zyl.nettyhttp;


import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.zyl.nettyhttp.pojo.User;
import com.zyl.nettyhttp.serialize.impl.JSONSerializer;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.AsciiString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.codec.Charsets;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import io.netty.buffer.Unpooled;

import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * @author james
 */
@Slf4j
public class HttpHelloWorldServerHandler extends SimpleChannelInboundHandler<HttpObject> {

    /**
     * HttpHeaders headers, HttpRequest request, FullHttpRequest fullRequest: 用于存储请求头和不同类型的HTTP请求。
     */
    private HttpHeaders headers;
    private HttpRequest request;
    private FullHttpRequest fullRequest;

    /**
     * FAVICON_ICO: 常量用于图标请求。
     * CONTENT_TYPE, CONTENT_LENGTH, CONNECTION, KEEP_ALIVE: 缓存的 AsciiString 常量，用于设置响应头。
     */
    private static final String FAVICON_ICO = "/favicon.ico";
    private static final AsciiString CONTENT_TYPE = AsciiString.cached("Content-Type");
    private static final AsciiString CONTENT_LENGTH = AsciiString.cached("Content-Length");
    private static final AsciiString CONNECTION = AsciiString.cached("Connection");
    private static final AsciiString KEEP_ALIVE = AsciiString.cached("keep-alive");


    /**
     * 该方法在接收到消息时被调用，处理HTTP请求并生成相应的响应。
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        User user = new User();
        user.setUserName("sanshengshui");
        user.setDate(new Date());
        //如果 msg 是 HttpRequest 的实例，则将其转换为 HttpRequest。
        if (msg instanceof HttpRequest){
            request = (HttpRequest) msg;
            headers = request.headers();
            //从请求中提取URI并记录。如果URI是 /favicon.ico，则立即返回以忽略请求。
            String uri = request.uri();
            log.info("http uri: "+ uri);
            if (uri.equals(FAVICON_ICO)){
                return;
            }
            //确定请求方法是GET还是POST
            HttpMethod method = request.method();
            if (method.equals(HttpMethod.GET)){
                //使用 QueryStringDecoder 解码查询参数并记录它们。
                QueryStringDecoder queryDecoder = new QueryStringDecoder(uri, Charsets.toCharset(CharEncoding.UTF_8));
                Map<String, List<String>> uriAttributes = queryDecoder.parameters();
                //此处仅打印请求参数（你可以根据业务需求自定义处理）
                for (Entry<String, List<String>> attr : uriAttributes.entrySet()) {
                    for (String attrVal : attr.getValue()) {
                        log.info(attr.getKey() + "=" + attrVal);
                    }
                }
                user.setMethod("get");
            }else if (method.equals(HttpMethod.POST)){
                //将 msg 转换为 FullHttpRequest 并调用 dealWithContentType() 根据内容类型处理请求体。
                //POST请求,由于你需要从消息体中获取数据,因此有必要把msg转换成FullHttpRequest
                fullRequest = (FullHttpRequest)msg;
                //根据不同的Content_Type处理body数据
                dealWithContentType();
                user.setMethod("post");

            }
            //将 User 对象序列化为JSON。
            JSONSerializer jsonSerializer = new JSONSerializer();
            byte[] content = jsonSerializer.serialize(user);
            //创建一个包含序列化JSON内容的 FullHttpResponse
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(content));
            //设置响应头 Content-Type 和 Content-Length
            response.headers().set(CONTENT_TYPE, "text/plain");
            response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
            //确定是否需要保持连接，并设置相应的头
            boolean keepAlive = HttpUtil.isKeepAlive(request);
            //将响应写回客户端，如果需要，则关闭连接
            if (!keepAlive) {
                ctx.write(response).addListener(ChannelFutureListener.CLOSE);
            } else {
                response.headers().set(CONNECTION, KEEP_ALIVE);
                ctx.write(response);
            }
        }
    }



    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    /**
     * 简单处理常用几种 Content-Type 的 POST 内容（可自行扩展）
     * @throws Exception
     */
    private void dealWithContentType() throws Exception{
        String contentType = getContentType();
        //可以使用HttpJsonDecoder
        if(contentType.equals("application/json")){
            //解析JSON字符串并记录每个键值对
            String jsonStr = fullRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
            JSONObject obj = JSON.parseObject(jsonStr);
            for(Entry<String, Object> item : obj.entrySet()){
                log.info(item.getKey()+"="+item.getValue().toString());
            }

        }else if(contentType.equals("application/x-www-form-urlencoded")){
            //使用 QueryStringDecoder 解码请求体并记录每个键值对
            //方式一：使用 QueryStringDecoder
			String jsonStr = fullRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));
			QueryStringDecoder queryDecoder = new QueryStringDecoder(jsonStr, false);
			Map<String, List<String>> uriAttributes = queryDecoder.parameters();
            for (Entry<String, List<String>> attr : uriAttributes.entrySet()) {
                for (String attrVal : attr.getValue()) {
                    log.info(attr.getKey() + "=" + attrVal);
                }
            }

        }else if(contentType.equals("multipart/form-data")){
            //用于文件上传的占位符
            //TODO 用于文件上传
        }else{
            //do nothing...
        }
    }

    /**
     * 从请求头中提取内容类型。
     * @return
     */
    private String getContentType(){
        //获取 Content-Type 头
        String typeStr = headers.get("Content-Type").toString();
        //通过 ; 分割内容类型字符串并返回第一个部分
        String[] list = typeStr.split(";");
        return list[0];
    }

}
